﻿using NUnit.Framework;
using Sasa.QualityTools.CodeAnalysis.Core.Models;
using System;
using System.IO;
using System.Linq;

namespace Sasa.QualityTools.CodeAnalysis.Core.Tests.Unit.Models
{
    [TestFixture]
    public class CodeAnalysisConfigurationTest
    {
        [TestCase(null)]
        [TestCase("")]
        [TestCase(" ")]
        [ExpectedException(typeof(ArgumentException))]
        public void LoadShouldThrowExceptionWhenPathIsInvalid(string path)
        {
            CodeAnalysisConfiguration.Load(path);
        }

        [Test]
        public void LoadShouldReturnDefaultConfigurationWhenLoadFailed()
        {
            var contents = new string[] {
                @"<?xml version=""1.0"" encoding=""utf-8""?>",
                @"<CodeAnalysisConfiguration Version=""1.2.0"" Enable=""true"">",
                @"    <EnableSearchGAC Override=""true"" Value=""false"" />"
            };

            var testee = CreateCodeAnalysisConfiguration(contents);
            Assert.AreEqual(true, testee.EnableCodeAnalysis);
            Assert.AreEqual(false, testee.OverrideEnableSearchGac);
            Assert.AreEqual(false, testee.OverrideIgnoreGeneratedCode);
            Assert.AreEqual(false, testee.OverrideTimeout);
            Assert.AreEqual(false, testee.OverrideRuleSet);
            Assert.AreEqual(OverrideMode.Inherit, testee.OverrideFilterPatterns);
            Assert.AreEqual(true, testee.EnableSearchGac);
            Assert.AreEqual(true, testee.IgnoreGeneratedCode);
            Assert.AreEqual(60, testee.Timeout);
            Assert.IsNull(testee.RuleSet);
            Assert.AreEqual(4, testee.FilterPatterns.Count);
            Assert.AreEqual(@"^.+\.[Dd]esigner\.[^\.]+$", testee.FilterPatterns.ElementAt(0));
            Assert.AreEqual(@"^.+\.[Gg]enerated\.[^\.]+$", testee.FilterPatterns.ElementAt(1));
            Assert.AreEqual(@"^.+\.g\.[^\.]+$", testee.FilterPatterns.ElementAt(2));
            Assert.AreEqual(@"^.+\.g\.i\.[^\.]+$", testee.FilterPatterns.ElementAt(3));
        }

        [Test]
        public void LoadShouldCreateInstanceFromConfigFileVer1_2_0()
        {
            var contents = new string[] {
                @"<?xml version=""1.0"" encoding=""utf-8""?>",
                @"<CodeAnalysisConfiguration Version=""1.2.0"" Enable=""true"">",
                @"    <EnableSearchGAC Override=""true"" Value=""false"" />",
                @"    <IgnoreGeneratedCode Override=""true"" Value=""false"" />",
                @"    <Timeout Override=""true"" Value=""120"" />",
                @"    <RuleSet Override=""true"" Value=""Bundled;all.ruleset"" />",
                @"</CodeAnalysisConfiguration>" };

            var testee = CreateCodeAnalysisConfiguration(contents);
            Assert.AreEqual(true, testee.EnableCodeAnalysis);
            Assert.AreEqual(true, testee.OverrideEnableSearchGac);
            Assert.AreEqual(true, testee.OverrideIgnoreGeneratedCode);
            Assert.AreEqual(true, testee.OverrideTimeout);
            Assert.AreEqual(true, testee.OverrideRuleSet);
            Assert.AreEqual(OverrideMode.Inherit, testee.OverrideFilterPatterns);
            Assert.AreEqual(false, testee.EnableSearchGac);
            Assert.AreEqual(false, testee.IgnoreGeneratedCode);
            Assert.AreEqual(120, testee.Timeout);
            Assert.AreEqual(GetRuleSetPath("all.ruleset"), testee.RuleSet);
            Assert.AreEqual(0, testee.FilterPatterns.Count);
        }

        [Test]
        public void LoadShouldCreateInstanceFromConfigFileVer1_3_0()
        {
            var contents = new string[] {
                @"<?xml version=""1.0"" encoding=""utf-8""?>",
                @"<CodeAnalysisConfiguration Version=""1.3.0"" Enable=""true"">",
                @"    <EnableSearchGAC Override=""true"" Value=""false"" />",
                @"    <IgnoreGeneratedCode Override=""true"" Value=""false"" />",
                @"    <Timeout Override=""true"" Value=""120"" />",
                @"    <RuleSet Override=""true"" Value=""Bundled;all.ruleset"" />",
                @"    <EnableCodeAnalysisOnBuild Override=""true"" Value=""true"" />",
                @"</CodeAnalysisConfiguration>" };

            var testee = CreateCodeAnalysisConfiguration(contents);
            Assert.AreEqual(true, testee.EnableCodeAnalysis);
            Assert.AreEqual(true, testee.OverrideEnableSearchGac);
            Assert.AreEqual(true, testee.OverrideIgnoreGeneratedCode);
            Assert.AreEqual(true, testee.OverrideTimeout);
            Assert.AreEqual(true, testee.OverrideRuleSet);
            Assert.AreEqual(OverrideMode.Inherit, testee.OverrideFilterPatterns);
            Assert.AreEqual(false, testee.EnableSearchGac);
            Assert.AreEqual(false, testee.IgnoreGeneratedCode);
            Assert.AreEqual(120, testee.Timeout);
            Assert.AreEqual(GetRuleSetPath("all.ruleset"), testee.RuleSet);
            Assert.AreEqual(0, testee.FilterPatterns.Count);
        }

        [Test]
        public void LoadShouldCreateInstanceFromConfigFileVer2_0()
        {
            var contents = new string[] {
                @"<?xml version=""1.0"" encoding=""utf-8""?>",
                @"<CodeAnalysisConfiguration Version=""2.0"" Enable=""true"">",
                @"    <EnableSearchGAC Override=""true"" Value=""false"" />",
                @"    <IgnoreGeneratedCode Override=""true"" Value=""false"" />",
                @"    <Timeout Override=""true"" Value=""120"" />",
                @"    <RuleSet Override=""true"" Value=""Bundled;all.ruleset"" />",
                @"    <EnableCodeAnalysisOnBuild Override=""true"" Value=""true"" />",
                @"    <FilterPatterns Override=""Merge"">",
                @"        <FilterPattern Value=""foo"" />",
                @"        <FilterPattern Value=""b&quot;a&quot;r"" />",
                @"    </FilterPatterns>",
                @"</CodeAnalysisConfiguration>" };

            var testee = CreateCodeAnalysisConfiguration(contents);
            Assert.AreEqual(true, testee.EnableCodeAnalysis);
            Assert.AreEqual(true, testee.OverrideEnableSearchGac);
            Assert.AreEqual(true, testee.OverrideIgnoreGeneratedCode);
            Assert.AreEqual(true, testee.OverrideTimeout);
            Assert.AreEqual(true, testee.OverrideRuleSet);
            Assert.AreEqual(OverrideMode.Merge, testee.OverrideFilterPatterns);
            Assert.AreEqual(false, testee.EnableSearchGac);
            Assert.AreEqual(false, testee.IgnoreGeneratedCode);
            Assert.AreEqual(120, testee.Timeout);
            Assert.AreEqual(GetRuleSetPath("all.ruleset"), testee.RuleSet);
            Assert.AreEqual(2, testee.FilterPatterns.Count);
            Assert.AreEqual("foo", testee.FilterPatterns.ElementAt(0));
            Assert.AreEqual("b\"a\"r", testee.FilterPatterns.ElementAt(1));
        }

        [Test]
        public void SaveShouldStoreConfigValueCorrectly()
        {
            var path = GetType().Name + ".xml";
            var testee = CodeAnalysisConfiguration.Load(path);
            testee.EnableCodeAnalysis = false;
            testee.OverrideEnableSearchGac = true;
            testee.OverrideIgnoreGeneratedCode = true;
            testee.OverrideTimeout = true;
            testee.OverrideRuleSet = true;
            testee.OverrideFilterPatterns = OverrideMode.Merge;
            testee.EnableSearchGac = false;
            testee.IgnoreGeneratedCode = false;
            testee.Timeout = 120;
            testee.RuleSet = GetRuleSetPath("all.ruleset");
            testee.FilterPatterns.Add("foo");
            testee.FilterPatterns.Add("b\"a\"r");

            testee.Save();
            var actual = File.ReadAllLines(path);

            var expected = new string[] {
                @"<?xml version=""1.0"" encoding=""utf-8""?>",
                @"<CodeAnalysisConfiguration Version=""2.0"" Enable=""false"">",
                @"    <EnableSearchGAC Override=""true"" Value=""false"" />",
                @"    <IgnoreGeneratedCode Override=""true"" Value=""false"" />",
                @"    <Timeout Override=""true"" Value=""120"" />",
                @"    <RuleSet Override=""true"" Value=""Bundled;all.ruleset"" />",
                @"    <FilterPatterns Override=""merge"">",
                @"        <FilterPattern Value=""^.+\.[Dd]esigner\.[^\.]+$"" />",
                @"        <FilterPattern Value=""^.+\.[Gg]enerated\.[^\.]+$"" />",
                @"        <FilterPattern Value=""^.+\.g\.[^\.]+$"" />",
                @"        <FilterPattern Value=""^.+\.g\.i\.[^\.]+$"" />",
                @"        <FilterPattern Value=""foo"" />",
                @"        <FilterPattern Value=""b&quot;a&quot;r"" />",
                @"    </FilterPatterns>",
                @"</CodeAnalysisConfiguration>" };

            CollectionAssert.AreEqual(expected.Select(o => o.Trim()).ToList(), actual.Select(o => o.Trim()).ToList());
        }

        [Test]
        public void OverrideShouldReplaceValuesWhenOverrideProperiesAreTrue()
        {
            var parent = CodeAnalysisConfiguration.Load("parent.xml");
            var child = CodeAnalysisConfiguration.Load("child.xml");

            parent.EnableSearchGac = true;
            parent.IgnoreGeneratedCode = true;
            parent.Timeout = 180;
            parent.RuleSet = "parent.ruleset";

            child.EnableSearchGac = false;
            child.OverrideEnableSearchGac = true;
            child.IgnoreGeneratedCode = false;
            child.OverrideIgnoreGeneratedCode = true;
            child.Timeout = 0;
            child.OverrideTimeout = true;
            child.RuleSet = "child.ruleset";
            child.OverrideRuleSet = true;

            child.Override(parent);

            Assert.IsFalse(child.EnableSearchGac);
            Assert.IsFalse(child.IgnoreGeneratedCode);
            Assert.AreEqual(0, child.Timeout);
            Assert.AreEqual("child.ruleset", child.RuleSet);
        }

        [Test]
        public void OverrideShouldReplaceValuesWhenOverrideProperiesAreFalse()
        {
            var parent = CodeAnalysisConfiguration.Load("parent.xml");
            var child = CodeAnalysisConfiguration.Load("child.xml");

            parent.EnableSearchGac = true;
            parent.IgnoreGeneratedCode = true;
            parent.Timeout = 180;
            parent.RuleSet = "parent.ruleset";

            child.EnableSearchGac = false;
            child.OverrideEnableSearchGac = false;
            child.IgnoreGeneratedCode = false;
            child.OverrideIgnoreGeneratedCode = false;
            child.Timeout = 0;
            child.OverrideTimeout = false;
            child.RuleSet = "child.ruleset";
            child.OverrideRuleSet = false;

            child.Override(parent);

            Assert.IsTrue(child.EnableSearchGac);
            Assert.IsTrue(child.IgnoreGeneratedCode);
            Assert.AreEqual(180, child.Timeout);
            Assert.AreEqual("parent.ruleset", child.RuleSet);
        }

        [Test]
        public void OverrideShouldReplaceFilterPatternsWhenModeIsReplace()
        {
            var parent = CodeAnalysisConfiguration.Load("parent.xml");
            var child = CodeAnalysisConfiguration.Load("child.xml");

            parent.FilterPatterns.Clear();
            parent.FilterPatterns.Add("a");
            parent.FilterPatterns.Add("b");
            child.FilterPatterns.Clear();
            child.FilterPatterns.Add("1");
            child.FilterPatterns.Add("2");
            child.OverrideFilterPatterns = OverrideMode.Replace;
            child.Override(parent);

            Assert.AreEqual(2, child.FilterPatterns.Count);
            Assert.AreEqual("1", child.FilterPatterns.ElementAt(0));
            Assert.AreEqual("2", child.FilterPatterns.ElementAt(1));
        }

        [Test]
        public void OverrideShouldNotReplaceFilterPatternsWhenModeIsInherit()
        {
            var parent = CodeAnalysisConfiguration.Load("parent.xml");
            var child = CodeAnalysisConfiguration.Load("child.xml");

            parent.FilterPatterns.Clear();
            parent.FilterPatterns.Add("a");
            parent.FilterPatterns.Add("b");
            child.FilterPatterns.Clear();
            child.FilterPatterns.Add("1");
            child.FilterPatterns.Add("2");
            child.OverrideFilterPatterns = OverrideMode.Inherit;
            child.Override(parent);

            Assert.AreEqual(2, child.FilterPatterns.Count);
            Assert.AreEqual("a", child.FilterPatterns.ElementAt(0));
            Assert.AreEqual("b", child.FilterPatterns.ElementAt(1));
        }

        [Test]
        public void OverrideShouldMergeFilterPatternsWhenModeIsMerge()
        {
            var parent = CodeAnalysisConfiguration.Load("parent.xml");
            var child = CodeAnalysisConfiguration.Load("child.xml");

            parent.FilterPatterns.Clear();
            parent.FilterPatterns.Add("a");
            parent.FilterPatterns.Add("b");
            child.FilterPatterns.Clear();
            child.FilterPatterns.Add("1");
            child.FilterPatterns.Add("2");
            child.OverrideFilterPatterns = OverrideMode.Merge;
            child.Override(parent);

            Assert.AreEqual(4, child.FilterPatterns.Count);
            Assert.AreEqual("1", child.FilterPatterns.ElementAt(0));
            Assert.AreEqual("2", child.FilterPatterns.ElementAt(1));
            Assert.AreEqual("a", child.FilterPatterns.ElementAt(2));
            Assert.AreEqual("b", child.FilterPatterns.ElementAt(3));
        }

        private CodeAnalysisConfiguration CreateCodeAnalysisConfiguration(params string[] contents)
        {
            var path = GetType().Name + ".xml";
            File.WriteAllLines(path, contents);
            return CodeAnalysisConfiguration.Load(path);
        }

        private string GetRuleSetPath(string fileName)
        {
            return Path.GetFullPath(Path.Combine(Environment.GetEnvironmentVariable("VS100COMNTOOLS"), "..", "..", "Team Tools", "Static Analysis Tools", "Rule Sets", fileName));
        }
    }
}
